#ifndef __XGU_DELEGATE10_H__
#define __XGU_DELEGATE10_H__

#include "Functor.h"
#include "MemberFunctor.h"

#include <vector>
using std::vector;

#include <cassert>

namespace xgu
{
	// -----------------------------------------------------------
	// 10 Arguments
	template <typename TRet = void,	
		typename TArg0 = void, 
		typename TArg1 = void, 
		typename TArg2 = void, 
		typename TArg3 = void, 
		typename TArg4 = void, 
		typename TArg5 = void, 
		typename TArg6 = void, 
		typename TArg7 = void, 
		typename TArg8 = void, 
		typename TArg9 = void
	>
	class Delegate
	{
	public:
		typedef vector<TRet> ReturnType;

		Delegate() {}
		virtual ~Delegate()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
		}

		Delegate(const Delegate<TRet, 
			TArg0, TArg1, TArg2, TArg3, TArg4, 
			TArg5, TArg6, TArg7, TArg8, TArg9>& delegate)
		{
			Add(delegate);
		}

		Delegate& operator=(const Delegate<TRet>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}

		template <typename TClass>
		Delegate(TClass& C, TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Add<TClass>(C, F);
		}
		template <typename TClass>
		Delegate(TClass* pC, TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Add<TClass>(pC, F);
		}

		Delegate& operator += (const Delegate<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& Value)
		{
			Add(Value);
			return *this;
		}
		Delegate& operator -= (const Delegate<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& Value)
		{
			Remove(Value);
			return *this;
		}

		virtual vector<TRet> operator() (TArg1 a0, TArg2 a1, TArg2 a2, TArg2 a3, TArg2 a4,
			TArg2 a5, TArg2 a6, TArg2 a7, TArg2 a8, TArg2 a9) const
		{
			vector<TRet> ret;
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = delegates[i];
				assert(f);
				ret.push_back((*f)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9));
			}
			return ret;
		}


		template<typename TClass>
		void Add(TClass& C, TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>* 
				f = new MemberFunctor<TClass, TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		template<typename TClass>
		void Add(TClass* pC, TRet (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			assert(pC);
			Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>* 
				f = new MemberFunctor<TClass, TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		void Add(const Delegate<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		void Remove(const Delegate<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = D.delegates[i];
				assert(f);
				Remove(f);
			}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}

	protected:
		void Add(const Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}
		void Remove(const Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>* F)
		{
			assert(F);
			for (vector<Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>*>::const_iterator 
				iter = delegates.begin();
				iter != delegates.end(); 
				++iter)
			{
				const Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}

	private:
		vector<Functor<TRet, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>*> delegates;
	};

	// ------------------------------------------------------------------
	// Void Return Type Specialization

	template <	
		typename TArg0, 
		typename TArg1, 
		typename TArg2, 
		typename TArg3, 
		typename TArg4, 
		typename TArg5, 
		typename TArg6, 
		typename TArg7, 
		typename TArg8, 
		typename TArg9
	>
	class Delegate<void, TArg0, TArg1, TArg2, TArg3, TArg4, 
						TArg5, TArg6, TArg7, TArg8, TArg9>
	{
	public:

		typedef void ReturnType;

		Delegate() {}
		virtual ~Delegate()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
		}

		Delegate(const Delegate<ReturnType, 
			TArg0, TArg1, TArg2, TArg3, TArg4, 
			TArg5, TArg6, TArg7, TArg8, TArg9>& delegate)
		{
			Add(delegate);
		}

		Delegate& operator=(const Delegate<ReturnType>& delegate)
		{
			RemoveAll();
			Add(delegate);
			return *this;
		}

		template <typename TClass>
		Delegate(TClass& C, void (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Add<TClass>(C, F);
		}
		template <typename TClass>
		Delegate(TClass* pC, void (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Add<TClass>(pC, F);
		}

		Delegate& operator += (const Delegate<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& Value)
		{
			Add(Value);
			return *this;
		}
		Delegate& operator -= (const Delegate<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& Value)
		{
			Remove(Value);
			return *this;
		}

		virtual void operator() (TArg1 a0, TArg2 a1, TArg2 a2, TArg2 a3, TArg2 a4,
			TArg2 a5, TArg2 a6, TArg2 a7, TArg2 a8, TArg2 a9) const
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = delegates[i];
				assert(f);
				(*f)(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
			}
		}


		template<typename TClass>
		void Add(TClass& C, void (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>* 
				f = new MemberFunctor<TClass, void, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>(C, F);
			assert(f);
			delegates.push_back(f);
		}
		template<typename TClass>
		void Add(TClass* pC, void (TClass::*F)(TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9))
		{
			assert(pC);
			Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>* 
				f = new MemberFunctor<TClass, void, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>(*pC, F);
			assert(f);
			delegates.push_back(f);
		}
		void Add(const Delegate<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Add(D.delegates[i]);
			}
		}
		void Remove(const Delegate<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>& D)
		{
			for (size_t i = 0; i < D.delegates.size(); ++i)
			{
				Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = D.delegates[i];
				assert(f);
				Remove(f);
			}
		}

		void RemoveAll()
		{
			for (size_t i = 0; i < delegates.size(); ++i)
			{
				assert(delegates[i] != NULL);
				delete delegates[i];
				delegates[i] = NULL;
			}
			delegates.clear();
		}

	protected:

		void Add(const Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>* F)
		{
			assert(F);
			delegates.push_back(F->Clone());
		}

		void Remove(const Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>* F)
		{
			assert(F);
			for (vector<Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
				TArg5, TArg6, TArg7, TArg8, TArg9>*>::const_iterator 
				iter = delegates.begin();
			iter != delegates.end(); 
			++iter)
			{
				const Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
					TArg5, TArg6, TArg7, TArg8, TArg9>* f = *iter;
				assert(f);
				if (*f == *F)
				{
					delete *iter;
					delegates.erase(iter);
					break;
				}
			}
		}

	private:
		vector<Functor<void, TArg0, TArg1, TArg2, TArg3, TArg4,
			TArg5, TArg6, TArg7, TArg8, TArg9>*> delegates;
	};


}

#endif